package com.lin.manager.tool.service.impl;

import com.lin.manager.common.ex.BizException;
import com.lin.manager.tool.model.TableVo;
import com.lin.manager.tool.service.GeneratorCodeService;
import com.lin.manager.tool.service.GeneratorDbService;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author linjiang
 * @date 2024/5/19
 * @fn
 */
@Service
public class GeneratorCodeServiceImpl implements GeneratorCodeService {
    private static Map<String, String> templateMap = new HashMap<>();

    {
        templateMap.put("entity.${className}Entity#java", "vm/entity.vm");
//        templateMap.put("service.${className}IService#java", "vm/iService.vm");
//        templateMap.put("service.impl.${className}IServiceImpl#java", "vm/iServiceImpl.vm");
//        templateMap.put("controller.${className}Controller#java", "vm/controller.vm");
    }

    @Autowired
    GeneratorDbService generatorDbService;
    @Value("${gen.project}")
    private String project;

    @Override
    public Map<String, String> genByTableId(Integer operate, Integer tableId, HttpServletResponse response) {
        // 获取业务表
        TableVo tableVo = generatorDbService.selectTableInfoById(tableId);
        // 生成代码
        Map<String, String> result = generateCode(tableVo);
        if (operate == 1) {
            downloadCode(result, response);
        }
        return result;
    }

    @Override
    public Map<String, String> previewByTableId(Integer operate, Integer tableId) {
        Map<String, String> result = this.genByTableId(operate, tableId, null);

        Map<String, String> var1 = new HashMap<>();
        result.forEach((k, v) -> {
            String newK = k.substring(k.lastIndexOf(".") + 1).replace("#", ".");
            var1.put(newK, v);
        });

        System.out.println(var1);

        return var1;
    }

    @Override
    public void genManyByTableId(List<Integer> tableIds, HttpServletResponse response) {
        Map<String, String> finalyResult = new HashMap<>();
        for (Integer tableId : tableIds) {
            TableVo tableVo = generatorDbService.selectTableInfoById(tableId);
            Map<String, String> result = generateCode(tableVo);
            finalyResult.putAll(result);
        }

        if (!finalyResult.isEmpty()) {
            downloadCode(finalyResult, response);
        }
    }

    /**
     * 下载代码
     *
     * @param result
     * @param response
     */
    private void downloadCode(Map<String, String> result, HttpServletResponse response) {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(stream);

        result.forEach((k, v) -> {
            String disk = (project + k)
                    .replace(".", "/")
                    .replace("..", "/")
                    .replace("#", ".");
            try {
                zip.putNextEntry(new ZipEntry(disk));
                IOUtils.write(v, zip, StandardCharsets.UTF_8);
                zip.flush();
                zip.closeEntry();
            } catch (IOException e) {
                e.printStackTrace();
                throw new BizException("下载失败");
            }
        });

        response.reset();
        response.setHeader("Content-Disposition", "attachment; filename=" + project + ".zip");
        response.setContentType("application/octet-stream; charset=UTF-8");
        try {
            IOUtils.write(stream.toByteArray(), response.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
            throw new BizException("下载失败");
        }
    }

    /**
     * 生成代码
     *
     * @param tableVo
     */
    private Map<String, String> generateCode(TableVo tableVo) {
        // 初始化模板引擎
        this.velocityInit();

        // 构建Velocity上下文
        VelocityContext context = createVelocityContext(tableVo);

        // 合并并返回结果
        Map<String, String> mergeResult = merge(context);
        return mergeResult;
    }


    /**
     * 初始化Velocity
     */
    public void velocityInit() {
        Properties prop = new Properties();
        prop.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        prop.setProperty(Velocity.INPUT_ENCODING, "utf-8");
        Velocity.init(prop);
    }

    /**
     * 构建Velocity上下文
     *
     * @param tableVo
     * @return
     */
    private VelocityContext createVelocityContext(TableVo tableVo) {
        VelocityContext context = new VelocityContext();
        // 基本信息
        context.put("package", tableVo.getPackageName());
        context.put("url", tableVo.getPackageName());
        context.put("module", tableVo.getModule());
        context.put("author", tableVo.getAuthor());
        context.put("tableName", tableVo.getTableName());
        context.put("className", tableVo.getClassName());
        context.put("path", StringUtils.isEmpty(tableVo.getPath()) ? "" : tableVo.getPath());
        context.put("date", LocalDate.now());

        // 字段信息
        context.put("columns", tableVo.getGenColumns());
        return context;
    }

    /**
     * 合并
     *
     * @param context
     */
    private Map<String, String> merge(VelocityContext context) {
        Map<String, String> result = new HashMap<>();
        // 定义磁盘路径模板
        StringBuilder builder = new StringBuilder("${path}.src.main.java.${url}.${module}");
        templateMap.forEach((k, v) -> {
            Template template = Velocity.getTemplate(v);

            StringWriter vmWriter = new StringWriter();
            template.merge(context, vmWriter);

            // 比如 entity.xxx#java -> entity.vm
            builder.append(".").append(k);

            StringWriter diskWriter = new StringWriter();
            Velocity.evaluate(context, diskWriter, "", builder.toString());
            result.put(diskWriter.toString(), vmWriter.toString());
        });

        return result;
    }
}
